# -*- coding: utf-8 -*-
"""
    xfree.tokens.serializer
    ~~~~~~~~~~~~~~~~~~~~~
    文件描述：这个模块实现了xfree的令牌序列化相关功能。
    作者：xfree
    邮箱：cy_83k@qq.com
"""

import datetime

import jwt

from..core import tokens

# 默认令牌有效期为1小时
_DEFAULT_EXPIRY = datetime.timedelta(hours=1)


class XFreeTokenSerializer(tokens.TokenSerializer):
    """
    xfree的默认令牌序列化器。它生成具有时间敏感性的JWT（JSON Web Tokens），
    默认情况下，令牌在1小时后过期。

    它从xfree.core.tokens.Token实例创建令牌，并在加载令牌时创建该类的实例。

    在加载令牌时，如果出现与令牌本身相关的错误，将抛出xfree.core.tokens.TokenError异常。
    非由令牌解析导致的异常将直接抛出。

    :param secret_key: 用于对令牌进行签名的密钥
    :param expiry: 令牌的有效期，可以是时间间隔（timedelta）或具体日期时间（datetime）
    """

    def __init__(self, secret_key, expiry=_DEFAULT_EXPIRY):
        self.secret_key = secret_key
        self.algorithm = "HS256"

        # 如果expiry是时间间隔（timedelta）类型，则计算出令牌的过期时间
        if isinstance(expiry, datetime.timedelta):
            self.expiry = datetime.datetime.now(datetime.UTC) + expiry
        # 如果expiry是日期时间（datetime）类型，则直接使用
        elif isinstance(expiry, datetime.datetime):
            self.expiry = expiry
        else:
            # 如果expiry类型不正确，抛出类型错误异常
            raise TypeError("'expiry'必须是timedelta或datetime类型")

    def dumps(self, token):
        """
        将xfree.core.tokens.Token实例转换为文本序列化的JWT。

        :param token: 要转换为JWT的令牌
        :return: 完全序列化的令牌（字符串）
        """
        return jwt.encode(
            # 构造JWT的负载（payload），包含用户ID、操作类型和过期时间
            payload={"id": token.user_id, "op": token.operation, "exp": self.expiry},
            key=self.secret_key,
            algorithm=self.algorithm,
        )

    def loads(self, raw_token):
        """
        将JWT转换为xfree.core.tokens.Token实例。

        如果令牌由于格式错误、被篡改或过期而无效，将抛出xfree.core.tokens.TokenError异常。
        与令牌解析无关的错误将直接抛出。

        :param raw_token: 要解析的JWT
        :return: 解析后的令牌（xfree.core.tokens.Token实例）
        """
        try:
            # 尝试解码JWT
            parsed = jwt.decode(
                raw_token, key=self.secret_key, algorithms=[self.algorithm]
            )
        except jwt.ExpiredSignatureError:
            # 如果令牌过期，抛出过期相关的TokenError异常
            raise tokens.TokenError.expired()
        except jwt.DecodeError:  # pragma: no branch
            # 如果解码错误（通常意味着令牌格式错误或被篡改），抛出无效相关的TokenError异常
            raise tokens.TokenError.invalid()
        # 理论上，jwt.DecodeError应该捕获所有其他与解码相关的异常，
        # 但由于它是PyJWT的根异常，这里捕获InvalidTokenError并重新抛出自定义异常
        except jwt.InvalidTokenError:  # pragma: no cover
            raise tokens.TokenError.bad()
        else:
            # 如果解码成功，返回解析后的令牌实例
            return tokens.Token(user_id=parsed["id"], operation=parsed["op"])